package com.example.examsystem.service;

import com.example.examsystem.dto.ExamDto;
import com.example.examsystem.dto.PageResponse;
import com.example.examsystem.exception.BusinessException;
import com.example.examsystem.model.Exam;
import com.example.examsystem.model.ExamStatus;
import com.example.examsystem.model.Question;
import com.example.examsystem.model.User;
import com.example.examsystem.repository.ExamRepository;
import com.example.examsystem.repository.QuestionRepository;
import com.example.examsystem.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 考试业务服务类
 * 处理考试相关的业务逻辑
 */
@Service
@RequiredArgsConstructor
public class ExamService {
    
    private final ExamRepository examRepository;
    private final QuestionRepository questionRepository;
    private final UserRepository userRepository;
    
    /**
     * 分页查询考试列表
     * @param page 页码（从0开始）
     * @param size 每页大小
     * @param query 搜索关键词
     * @param status 考试状态
     * @param subjectId 学科ID
     * @return 分页考试数据
     */
    public PageResponse<ExamDto> getExams(int page, int size, String query, ExamStatus status, Long subjectId) {
        // 创建分页参数，按创建时间倒序排列
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        
        // 执行分页查询
        Page<Exam> examPage = examRepository.findByQuery(query, status, subjectId, pageable);
        
        // 转换为DTO列表
        List<ExamDto> content = examPage.getContent().stream()
            .map(this::convertToDto)
            .collect(Collectors.toList());
        
        // 返回分页响应
        return new PageResponse<>(
            content,
            examPage.getNumber(),
            examPage.getSize(),
            examPage.getTotalElements(),
            examPage.getTotalPages()
        );
    }
    
    /**
     * 根据ID获取考试详情
     * @param id 考试ID
     * @return 考试详情
     */
    public ExamDto getExam(Long id) {
        Exam exam = examRepository.findById(id)
            .orElseThrow(() -> new BusinessException("考试不存在"));
        return convertToDto(exam);
    }
    
    /**
     * 创建新考试
     * @param examDto 考试数据传输对象
     * @return 创建后的考试信息
     */
    @Transactional
    public ExamDto createExam(ExamDto examDto) {
        // 获取当前登录用户
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        User currentUser = userRepository.findByUsername(username)
            .orElseThrow(() -> new BusinessException("用户不存在"));
        
        // 转换为实体对象
        Exam exam = convertToEntity(examDto);
        exam.setCreatedBy(currentUser.getId());
        exam.setStatus(ExamStatus.DRAFT); // 新建考试默认为草稿状态
        
        // 保存考试
        Exam savedExam = examRepository.save(exam);
        return convertToDto(savedExam);
    }
    
    /**
     * 更新考试信息
     * @param id 考试ID
     * @param examDto 更新的考试数据
     * @return 更新后的考试信息
     */
    @Transactional
    public ExamDto updateExam(Long id, ExamDto examDto) {
        // 查找现有考试
        Exam existingExam = examRepository.findById(id)
            .orElseThrow(() -> new BusinessException("考试不存在"));
        
        // 检查考试状态，已发布的考试不能修改基本信息
        if (existingExam.getStatus() == ExamStatus.PUBLISHED || 
            existingExam.getStatus() == ExamStatus.ONGOING) {
            throw new BusinessException("已发布或进行中的考试不能修改");
        }
        
        // 更新考试信息
        updateExamFromDto(existingExam, examDto);
        
        // 保存更新
        Exam savedExam = examRepository.save(existingExam);
        return convertToDto(savedExam);
    }
    
    /**
     * 删除考试
     * @param id 考试ID
     */
    @Transactional
    public void deleteExam(Long id) {
        Exam exam = examRepository.findById(id)
            .orElseThrow(() -> new BusinessException("考试不存在"));
        
        // 只有草稿状态的考试才能删除
        if (exam.getStatus() != ExamStatus.DRAFT) {
            throw new BusinessException("只有草稿状态的考试才能删除");
        }
        
        examRepository.deleteById(id);
    }
    
    /**
     * 发布考试
     * @param id 考试ID
     * @return 发布后的考试信息
     */
    @Transactional
    public ExamDto publishExam(Long id) {
        Exam exam = examRepository.findById(id)
            .orElseThrow(() -> new BusinessException("考试不存在"));
        
        // 检查考试状态
        if (exam.getStatus() != ExamStatus.DRAFT) {
            throw new BusinessException("只有草稿状态的考试才能发布");
        }
        
        // 验证考试信息完整性
        validateExamForPublish(exam);
        
        // 更新状态为已发布
        exam.setStatus(ExamStatus.PUBLISHED);
        Exam savedExam = examRepository.save(exam);
        
        return convertToDto(savedExam);
    }
    
    /**
     * 归档考试
     * @param id 考试ID
     * @return 归档后的考试信息
     */
    @Transactional
    public ExamDto archiveExam(Long id) {
        Exam exam = examRepository.findById(id)
            .orElseThrow(() -> new BusinessException("考试不存在"));
        
        // 只有已结束的考试才能归档
        if (exam.getStatus() != ExamStatus.FINISHED) {
            throw new BusinessException("只有已结束的考试才能归档");
        }
        
        exam.setStatus(ExamStatus.ARCHIVED);
        Exam savedExam = examRepository.save(exam);
        
        return convertToDto(savedExam);
    }
    
    /**
     * 验证考试发布前的完整性
     * @param exam 考试实体
     */
    private void validateExamForPublish(Exam exam) {
        if (exam.getTitle() == null || exam.getTitle().trim().isEmpty()) {
            throw new BusinessException("考试标题不能为空");
        }
        
        if (exam.getDuration() == null || exam.getDuration() <= 0) {
            throw new BusinessException("考试时长必须大于0");
        }
        
        if (exam.getStartTime() == null || exam.getEndTime() == null) {
            throw new BusinessException("考试开始时间和结束时间不能为空");
        }
        
        if (exam.getStartTime().isAfter(exam.getEndTime())) {
            throw new BusinessException("考试开始时间不能晚于结束时间");
        }
        
        if (exam.getQuestions() == null || exam.getQuestions().isEmpty()) {
            throw new BusinessException("考试必须包含至少一道题目");
        }
    }
    
    /**
     * 将实体转换为DTO
     * @param exam 考试实体
     * @return 考试DTO
     */
    private ExamDto convertToDto(Exam exam) {
        ExamDto dto = new ExamDto();
        dto.setId(exam.getId());
        dto.setTitle(exam.getTitle());
        dto.setDescription(exam.getDescription());
        dto.setSubjectId(exam.getSubjectId());
        dto.setSubjectName(exam.getSubject() != null ? exam.getSubject().getName() : null);
        dto.setDuration(exam.getDuration());
        dto.setTotalScore(exam.getTotalScore().doubleValue());
        dto.setPassingScore(exam.getPassingScore().doubleValue());
        dto.setStartTime(exam.getStartTime());
        dto.setEndTime(exam.getEndTime());
        dto.setStatus(exam.getStatus().name());
        dto.setAllowRetake(exam.getAllowRetake());
        dto.setMaxAttempts(exam.getMaxAttempts());
        dto.setShuffleQuestions(exam.getShuffleQuestions());
        dto.setShuffleOptions(exam.getShuffleOptions());
        dto.setCreatedBy(exam.getCreatedBy());
        dto.setCreatedByName(exam.getCreatedByUser() != null ? exam.getCreatedByUser().getFullName() : null);
        dto.setCreatedAt(exam.getCreatedAt());
        dto.setUpdatedAt(exam.getUpdatedAt());
        
        if (exam.getQuestions() != null) {
            dto.setQuestionIds(exam.getQuestions().stream()
                .map(Question::getId)
                .collect(Collectors.toList()));
        }
        
        return dto;
    }
    
    /**
     * 将DTO转换为实体
     * @param dto 考试DTO
     * @return 考试实体
     */
    private Exam convertToEntity(ExamDto dto) {
        Exam exam = new Exam();
        updateExamFromDto(exam, dto);
        return exam;
    }
    
    /**
     * 从DTO更新实体信息
     * @param exam 考试实体
     * @param dto 考试DTO
     */
    private void updateExamFromDto(Exam exam, ExamDto dto) {
        exam.setTitle(dto.getTitle());
        exam.setDescription(dto.getDescription());
        exam.setSubjectId(dto.getSubjectId());
        exam.setDuration(dto.getDuration());
        exam.setTotalScore(dto.getTotalScore() != null ? 
            java.math.BigDecimal.valueOf(dto.getTotalScore()) : null);
        exam.setPassingScore(dto.getPassingScore() != null ? 
            java.math.BigDecimal.valueOf(dto.getPassingScore()) : null);
        exam.setStartTime(dto.getStartTime());
        exam.setEndTime(dto.getEndTime());
        exam.setAllowRetake(dto.getAllowRetake());
        exam.setMaxAttempts(dto.getMaxAttempts());
        exam.setShuffleQuestions(dto.getShuffleQuestions());
        exam.setShuffleOptions(dto.getShuffleOptions());
        
        if (dto.getQuestionIds() != null && !dto.getQuestionIds().isEmpty()) {
            List<Question> questions = questionRepository.findAllById(dto.getQuestionIds());
            exam.setQuestions(questions);
        }
    }
}